using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace FrameworkX.ObjectSpace.Util
{
    internal class AESCryptoHelper
    {
        private RijndaelManaged m_symmetricKey = null;

        private const string DEFAULT_PASSWORD_PHRASE = "1234567890123456";
        //Hash algorithm used to generate password. Allowed values are: "MD5" and
        /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
        private const string HASH_ALGORITHM = "SHA1";
        //declare Iteration number as 2, it could be any positive integer number
        private const int ITERATION_NUMBER = 1;
        //declare the Key Size
        private const int KEY_SIZE = 256;
        //Initialization vector (or IV). This value is required to encrypt the
        // first block of plaintext data. For RijndaelManaged class IV must be 
        // exactly 16 ASCII characters long.
        private const string INIT_VECTOR = "@1B2c3D4e5F6g7H8";
        //input password phrase
        private string m_passPhrase = string.Empty;
        //optional, can be event empty 
        private const string SALT_VALUE = "78yuy@jkl";

        public AESCryptoHelper(string passwordPhrase)
		{
			//save the password phrase, it will be needed 
			//for later key generation
			m_passPhrase = passwordPhrase;
			//instantiate the encryption object
			m_symmetricKey = new RijndaelManaged();	
			//generate IV
			//m_symmetricKey.GenerateIV();
			// It is reasonable to set encryption mode to Cipher Block Chaining
			// (CBC). Use default options for other symmetric key parameters.
			m_symmetricKey.Mode = CipherMode.CBC;        
		}

        public string Encrypt(string plainText)
        {
            if (string.Empty.Equals(plainText))
            {
                return string.Empty;
            }
            //get the initial vector from the encryption object
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(INIT_VECTOR); //m_symmetricKey.IV;
            //salt is optional, so here we are passing just an
            //empty string
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(SALT_VALUE);

            // Convert our plaintext into a byte array.
            // Let us assume that plaintext contains UTF8-encoded characters.
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            // First, we must create a password, from which the key will be derived.
            // This password will be generated from the specified passphrase and 
            // salt value. The password will be created using the specified hash 
            // algorithm. Password creation can be done in several iterations.
            PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                    m_passPhrase,
                                                    saltValueBytes,
                                                    HASH_ALGORITHM,
                                                    ITERATION_NUMBER);

            // Use the password to generate pseudo-random bytes for the encryption
            // key. Specify the size of the key in bytes (instead of bits).
            byte[] keyBytes = password.GetBytes(KEY_SIZE / 8);


            ICryptoTransform encryptor = m_symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);

            // Define memory stream which will be used to hold encrypted data.
            MemoryStream memoryStream = new MemoryStream();

            // Define cryptographic stream (always use Write mode for encryption).
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                encryptor,
                CryptoStreamMode.Write);
            // Start encrypting.
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

            // Finish encrypting.
            cryptoStream.FlushFinalBlock();

            // Convert our encrypted data from a memory stream into a byte array.
            byte[] cipherTextBytes = memoryStream.ToArray();

            // Close both streams.
            memoryStream.Close();
            cryptoStream.Close();

            // Convert encrypted data into a base64-encoded string.
            string cipherText = Convert.ToBase64String(cipherTextBytes);

            // Return encrypted string.
            return cipherText;
        }

        public string Decrypt(string cipherText)
        {
            if (string.Empty.Equals(cipherText))
            {
                return string.Empty;
            }

            //get the initial vector from the encryption object
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(INIT_VECTOR);//m_symmetricKey.IV;
            //salt is optional, so here we are passing just an
            //empty string
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(SALT_VALUE);
            // Convert our ciphertext into a byte array.
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

            // First, we must create a password, from which the key will be derived.
            // This password will be generated from the specified passphrase and 
            // salt value. The password will be created using the specified hash 
            // algorithm. Password creation can be done in several iterations.
            PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                    m_passPhrase,
                                                    saltValueBytes,
                                                    HASH_ALGORITHM,
                                                    ITERATION_NUMBER);

            // Use the password to generate pseudo-random bytes for the encryption
            // key. Specify the size of the key in bytes (instead of bits).
            byte[] keyBytes = password.GetBytes(KEY_SIZE / 8);

            // Generate decryptor from the existing key bytes and initialization 
            // vector. Key size will be defined based on the number of the key 
            // bytes.
            ICryptoTransform decryptor = m_symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
            string plainText = string.Empty;
            try
            {

                // Define memory stream which will be used to hold encrypted data.
                MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

                // Define cryptographic stream (always use Read mode for encryption).
                CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor,
                    CryptoStreamMode.Read);

                // Since at this point we don't know what the size of decrypted data
                // will be, allocate the buffer long enough to hold ciphertext;
                // plaintext is never longer than ciphertext.
                byte[] plainTextBytes = new byte[cipherTextBytes.Length];

                // Start decrypting.
                int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);

                // Close both streams.
                memoryStream.Close();
                cryptoStream.Close();


                // Convert decrypted data into a string. 
                // Let us assume that the original plaintext string was UTF8-encoded.
                plainText = Encoding.UTF8.GetString(plainTextBytes,
                                                    0, decryptedByteCount);
            }
            catch (Exception exp)
            {
                throw new Exception("Correct Key was not Supplied.",exp);
            }

            // Return decrypted string.   
            return plainText;
        }
    }
}
